﻿using System;
using Audit.Core;
using Serilog;
using Serilog.Events;
using ILogger = Serilog.ILogger;

namespace Shared.Service.Logging.Serilog
{
    public class SerilogProvider : AuditDataProvider
    {
        public string AppName { get; set; }
        public Func<AuditEvent, ILogger> LoggerBuilder { get; set; }
        public Func<AuditEvent, LogEventLevel> LogLevelBuilder { get; set; }
        public Func<AuditEvent, object, object> LogMessageBuilder { get; set; }

        public SerilogProvider(string appName)
        {
            AppName = appName;
        }

        public SerilogProvider(string appName, Action<ISerilogConfigurator> config)
        {
            var logConfig = new SerilogConfigurator();
            if (config != null)
            {
                config.Invoke(logConfig);
                LoggerBuilder = logConfig.LoggerBuilder;
                LogLevelBuilder = logConfig.LogLevelBuilder;
                LogMessageBuilder = logConfig.MessageBuilder;
            }
            AppName = appName;
        }

        private ILogger GetLogger(AuditEvent auditEvent)
        {
            return LoggerBuilder?.Invoke(auditEvent) ?? new LoggerConfiguration()
                       .MinimumLevel.Information()
                       .Enrich.WithProperty("AppName", AppName).CreateLogger();
        }

        private LogEventLevel GetLogLevel(AuditEvent auditEvent)
        {
            return LogLevelBuilder?.Invoke(auditEvent) ?? (auditEvent.Environment.Exception != null ? LogEventLevel.Error : LogEventLevel.Information);
        }

        private object GetLogObject(AuditEvent auditEvent, object eventId)
        {
            if (LogMessageBuilder == null)
            {
                if (eventId != null)
                {
                    auditEvent.CustomFields["EventId"] = eventId;
                }
                return auditEvent.ToJson();
            }
            return LogMessageBuilder.Invoke(auditEvent, eventId);
        }

        private void Log(AuditEvent auditEvent, object eventId)
        {
            var logger = GetLogger(auditEvent);
            var level = GetLogLevel(auditEvent);
            var value = GetLogObject(auditEvent, eventId).ToString();

            //过滤健康检查
            if (value.IndexOf("/api/health", StringComparison.Ordinal) >= 0)
                return;

            //过滤metrics
            if (value.IndexOf("/metrics", StringComparison.Ordinal) >= 0)
                return;

            //过滤cap找不到消费者
            if (value.IndexOf("https://github.com/dotnetcore/CAP/issues/63", StringComparison.OrdinalIgnoreCase) >= 0)
                return;

            switch (level)
            {
                case LogEventLevel.Debug:
                    logger.Debug(value);
                    break;
                case LogEventLevel.Warning:
                    logger.Warning(value);
                    break;
                case LogEventLevel.Error:
                    logger.Error(value);
                    break;
                case LogEventLevel.Fatal:
                    logger.Fatal(value);
                    break;
                case LogEventLevel.Information:
                default:
                    logger.Information(value);
                    break;
            }
        }

        public override object InsertEvent(AuditEvent auditEvent)
        {
            var eventId = Guid.NewGuid();
            Log(auditEvent, eventId);
            return eventId;
        }

        public override void ReplaceEvent(object eventId, AuditEvent auditEvent)
        {
            Log(auditEvent, eventId);
        }
    }
}
